use serde::Serialize;

use crate::{
    error::AppErr,
    global::APP,
    serialport::{
        cmd::{self},
        proto::Frame,
        BodyDecode,
    }, utils::BitSet,
};

pub fn exec(frame: Frame) -> Result<(), AppErr> {
    let cmd = frame.cmd();
    match cmd {
        cmd::STATUS => on_status(frame),

        _ => Err(AppErr::Static("unknown cmd")),
    }
}

pub struct Status {
    pub version: u16,
    pub sensor: u16,
    pub analog: u16,
}

impl<'de> BodyDecode<'de> for Status {
    fn decode(buf_ref: &mut &[u8]) -> Option<Self>
    where
        Self: Sized,
    {
        let version = u16::decode(buf_ref)?;
        let sensor = u16::decode(buf_ref)?;
        let analog = u16::decode(buf_ref)?;
        Some(Status { version, sensor, analog })
    }
}

#[derive(Debug, Serialize)]
struct StatusInfoDescript {
    version: String,
    drop_sw1: bool,
    drop_sw2: bool,
    drop_sw3: bool,
    film_sw: bool,
    film_checked: bool,
    cut_top: bool,
    cut_bottom: bool,
    cut_checked: bool,
    drop_checked1: bool,
    drop_checked2: bool,
    drop_checked3: bool,
    leak: bool,
    waste_checked: bool,
    analog: u16,
}

impl StatusInfoDescript {

    pub fn new(status: &Status) -> Self {
        let version = format!("{:04x}", status.version);
        Self { 
            version,
            drop_sw1: status.sensor.is_set(0),
            drop_sw2: status.sensor.is_set(1),
            drop_sw3: status.sensor.is_set(2),
            film_sw: status.sensor.is_set(3),
            film_checked: status.sensor.is_set(4),
            cut_top: status.sensor.is_set(5),
            cut_bottom: status.sensor.is_set(6),
            cut_checked: status.sensor.is_set(7),
            drop_checked1: status.sensor.is_set(8),
            drop_checked2: status.sensor.is_set(9),
            drop_checked3: status.sensor.is_set(10),
            leak: status.sensor.is_set(11),
            waste_checked: status.sensor.is_set(12),
            analog: status.analog
        }
    }
}

fn on_status(frame: Frame) -> Result<(), AppErr> {
    let status: Status = frame.parse_cmd()?;
    let status = StatusInfoDescript::new(&status);
    APP.notify_drop(&status);
    Ok(())
}
